#!/usr/bin/env python3

import os
import platform
import sys
import numpy
import pylab
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import textedit
import qrc_resources


__version__ = "1.0.0"


class MainWindow(QMainWindow):
    

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.projdir = "."
	self.mdi = QWorkspace()
	self.setCentralWidget(self.mdi)

	self.printer = None

	self.sizeLabel = QLabel()
	self.sizeLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)

        projNewAction = self.createAction("&New Project...", self.projNew, None,  
	    None, "Create a project")
        fileNewAction = self.createAction("&New File...", self.fileNew, QKeySequence.New, 
	    "filenew", "Create a file")
	projOpenAction = self.createAction("&Open Project...", self.projOpen, None,
	    None, "Open an exsisting project")
	fileOpenAction = self.createAction("&Open File...", self.fileOpen, QKeySequence.Open, 
	    "fileopen", "Open an exsiting file")
	fileSaveAction = self.createAction("&Save", self.fileSave, QKeySequence.Save, 
	    "filesave", "Save the file")
	fileSaveAsAction = self.createAction("Save &As...", self.fileSaveAs, 
	    icon="filesaveas", tip="Save the file using a new name")
	fileSaveAllAction = self.createAction("Save A&ll", self.fileSaveAll, "filesave",
	    tip="Save all the files")
	filePrintAction = self.createAction("&Print", self.filePrint, QKeySequence.Print, 
	    "fileprint", "Print the file")
	fileQuitAction = self.createAction("&Quit", self.close, "Ctrl+Q", "filequit", 
            "Close the application")
	editCopyAction = self.createAction("&Copy", self.editCopy, QKeySequence.Copy,
	    "editcopy", "Copy text to the clipboard")
	editCutAction = self.createAction("Cu&t", self.editCut, QKeySequence.Cut,
	    "editcut", "Cut text to the clipboard")
	editPasteAction = self.createAction("&Paste", self.editPaste, QKeySequence.Paste,
	    "editpaste", "Paste in the clipboard's text")

        projMakeAction = self.createAction("&Make", self.projMake, None, 
	    "Project Make")
	projMakeCleanAction = self.createAction("&Make Clean", 
	    self.projMakeClean, None, "Project Make Clean")
	projRunAction = self.createAction("&Run", self.projRun, None, "Project Run")
	vcdViewAction = self.createAction("View VCD file", self.vcdView, None, "View vcd file with dinotrace")
	plotAction = self.createAction("Plot", self.plot, None, "Plot using matplot") 
	self.windowNextAction = self.createAction("&Next", self.mdi.activateNextWindow, 
	    QKeySequence.NextChild)
	self.windowPrevAction = self.createAction("&Previous", self.mdi.activatePreviousWindow,
            QKeySequence.PreviousChild)
	self.windowCascadeAction = self.createAction("Casca&de", self.mdi.cascade)
	self.windowTileAction = self.createAction("&Tile", self.mdi.cascade)
	self.windowRestoreAction = self.createAction("&Restore All", self.windowRestoreAll)
	self.windowMinimizeAction = self.createAction("&Iconize All", self.windowMinimizeAll)
	self.windowArrangeIconsAction = self.createAction("&Arrange Icons", 
	    self.mdi.arrangeIcons)
	self.windowCloseAction = self.createAction("&Close", self.mdi.closeActiveWindow,
            QKeySequence.Close)

	self.windowMapper = QSignalMapper(self)
	self.connect(self.windowMapper, SIGNAL("mapped(QWidget*)"), self.mdi, 
	    SLOT("setActiveWindow(QWidget*)"))

	helpAboutAction = self.createAction("About Verilator Gui", self.helpAbout)
	helpHelpAction = self.createAction("&Help", self.helpHelp, QKeySequence.HelpContents)
	
	fileMenu = self.menuBar().addMenu("&File")
	self.addActions(fileMenu, (projNewAction, fileNewAction, projOpenAction, 
	    fileOpenAction, fileSaveAction, fileSaveAsAction, 
	    fileSaveAllAction, None, filePrintAction, fileQuitAction))
	editMenu = self.menuBar().addMenu("&Edit")
	self.addActions(editMenu, (editCopyAction, editCutAction, editPasteAction))
	buildMenu = self.menuBar().addMenu("&Build")
	self.addActions(buildMenu, (projMakeAction, projMakeCleanAction, projRunAction))
	viewMenu = self.menuBar().addMenu("View")
        self.addActions(viewMenu, (vcdViewAction, plotAction))
	self.windowMenu = self.menuBar().addMenu("&Window")
	self.connect(self.windowMenu, SIGNAL("aboutToShow()"), self.updateWindowMenu)
	helpMenu = self.menuBar().addMenu("&Help")
	self.addActions(helpMenu, (helpAboutAction, helpHelpAction))

	fileToolbar = self.addToolBar("File")
	fileToolbar.setObjectName("FileToolBar")
	self.addActions(fileToolbar, (fileNewAction, fileOpenAction, fileSaveAction))
	editToolbar = self.addToolBar("Edit")
	editToolbar.setObjectName("EditToolBar")
	self.addActions(editToolbar, (editCopyAction, editCutAction, editPasteAction))
	fontToolbar = self.addToolBar("Font")
	fontToolbar.setObjectName("FontToolBar")
        fontComboBox = QFontComboBox()
	self.connect(fontComboBox, SIGNAL("currentFontChanged(QFont)"), self.setCurrentFont) 
	fontToolbar.addWidget(fontComboBox)
	fontsizeToolbar = self.addToolBar("FontSize")
	fontsizeToolbar.setObjectName("FontSizeToolBar")
	fontsizeComboBox = QComboBox()
	fontsizeList = ['10', '15', '20', '25', '30', '35', '40', '45', '50', 
	    '55', '60', '70', '75']
	for i in range(len(fontsizeList)):
	    fontsizeComboBox.insertItem(i, fontsizeList[i])
        self.connect(fontsizeComboBox, SIGNAL("activated(QString)"), self.setFontSize)
	fontsizeToolbar.addWidget(fontsizeComboBox)

	settings = QSettings()
	self.recentFiles = settings.value("RecentFiles").toStringList()
	self.restoreGeometry(settings.value("MainWindow/Geometry").toByteArray())
	self.restoreState(settings.value("MainWindow/State").toByteArray())

	status = self.statusBar()
	status.setSizeGripEnabled(False)
	status.addPermanentWidget(self.sizeLabel)
	status.showMessage("Ready", 5000)

	self.setWindowTitle("Verilator Gui")
	self.updateWindowMenu()
	QTimer.singleShot(0, self.loadFiles)


    def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False,
        signal="triggered()"):
        action = QAction(text, self)
	if icon is not None:
            action.setIcon(QIcon(":/{0}.png".format(icon)))
	if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
	    action.setStatusTip(tip)
        if slot is not None:
            self.connect(action, SIGNAL(signal), slot) 
        if checkable:
            action.setCheckable(True)
        return action


    def addActions(self, target, actions):
        for action in actions:
            if action is None:
                target.addSeparator()
	    else:
	        target.addAction(action)


    def closeEvent(self, event):
        failures = []
	for textEdit in self.mdi.windowList():
	    if textEdit.isModified():
	        try:
		    textEdit.save()
		except IOError as e:
		    failures.append(str(e))
        if failures and \
	    QMessageBox.warning(self, "Verilator Gui -- Save Error", 
	        "Failed to save{0}\nQuit anyway?".format("\n\t".join(failures)),
		QMessageBox.Yes|QMessageBox.No) == QMessageBox.No:
	    event.ignore()
	    return
        settings = QSettings()
	files = QStringList()
	for textEdit in self.mdi.windowList():
	    if not textEdit.filename.startsWith("Unnamed"):
	        files.append(textEdit.filename)
	settings.setValue("CurrentFiles", QVariant(files))
	settings.setValue("MainWindow/Geometry", QVariant(self.saveGeometry()))
	settings.setValue("MainWindow/State", QVariant(self.saveState()))
	self.mdi.closeAllWindows()


    def projNew(self):
        dirname = QFileDialog.getExistingDirectory(self, "Verilator Gui -- Create Project", 
		".",  QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
	if dirname.isEmpty():
	    return
        self.projdir = dirname
        return


    def fileNew(self):
        textEdit = textedit.TextEdit()
	self.mdi.addWindow(textEdit)
	textEdit.show()


    def projOpen(self):
        dirname = QFileDialog.getExistingDirectory(self, "Verilator Gui -- Open Project", 
	    ".", QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
	if not dirname.isEmpty():
	    self.projdir = dirname 
        return


    def fileOpen(self):
        filename = QFileDialog.getOpenFileName(self, "Verilator Gui -- Open File", self.projdir, \
            ("C++ files (*.cpp *.c++ *.h);; Verilog files (*.v);; files (*)"), None, 
	    QFileDialog.DontUseNativeDialog)
	if not filename.isEmpty():
	    for textEdit in self.mdi.windowList():
	        if textEdit.filename == filename:
		    self.mdi.setActiveWindow(textEdit)
		    break
	    else:
	        self.loadFile(filename)


    def loadFiles(self):
        if len(sys.argv) > 1:
	    for filename in sys.argv[1:31]:
	        filename = QString(filename)
		if QFileInfo(filename).isFile():
		    self.loadFile(filename)
		    QApplication.processEvents()
        else:
	    settings = QSettings()
	    files = settings.value("CurrentFiles").toStringList()
	    for filename in files:
	        filename = QString(filename)
		if QFile.exists(filename):
		    self.loadFile(filename)
		    QApplication.processEvents()


    def loadFile(self, filename=None):
        textEdit = textedit.TextEdit(filename)
	try:
	    textEdit.load()
	except EnvironmentError as e:
	    QMessageBox.warning(self, "Verilator Gui -- Load Error",
			    "Failed to load {0}: {1}".format(filename, e))
	    textEdit.close()
	    del textEdit
	else:
	    self.mdi.addWindow(textEdit)
	    textEdit.show()


    def fileSave(self):
        textEdit = self.mdi.activeWindow()
	if textEdit is None or not isinstance(textEdit, QTextEdit):
	    return
        if textEdit.filename.startsWith("Unnamed"):
	    return self.fileSaveAs()
        try:
	    textEdit.save()
	    return True
	except EnvironmentError as e:
	    QMessageBox.warning(self, "Verilator Gui -- Save Error", 
	        "Failed to save {0}: {1}".format(textEdit.filename, e))
	    return False


    def fileSaveAs(self):
        textEdit = self.mdi.activeWindow()
	if textEdit is None or not isinstance(textEdit, QTextEdit):
	    return
        filename = QFileDialog.getSaveFileName(self, "Verilator Gui -- Save File As", self.projdir, \
	    ("C++ files (*.cpp *.c++ *.h);; Verilog files (*.v);; files (*)"),
	    None, QFileDialog.DontUseNativeDialog)
	if not filename.isEmpty():
	    textEdit.filename = filename
	    return self.fileSave()
        return True


    def fileSaveAll(self):
        errors = []
	for textEdit in self.mdi.windowList():
	    if textEdit.isModified():
	        try:
		    textEdit.save()
		except EnvironmentError as e:
		    errors.append("{0}: {1}".format(textEdit.filename, e))
        if errors:
	    QMessageBox.warning(self, "Verilator Gui -- Save All Error", 
                "Failed to save\n{0}".format("\n".join(errors)))


    def filePrint(self):
	textEdit = self.mdi.activeWindow()
	if textEdit is None or not isinstance(textEdit, QTextEdit):
	    return
	if self.printer is None:
	    self.printer = QPrinter(QPrinter.HighResolution)
	    self.printer.setPageSize(QPrinter.Letter)
	    self.printer.setOutputFormat(QPrinter.PdfFormat)
	form = QPrintDialog(self.printer, self)
	if form.exec_():
	    textEdit.print_(self.printer)


    def editCopy(self):
	textEdit = self.mdi.activeWindow()
	if textEdit is None or not isinstance(textEdit, QTextEdit):
            return
        cursor = textEdit.textCursor()
	text = cursor.selectedText()
	if not text.isEmpty():
	    clipboard = QApplication.clipboard()
	    clipboard.setText(text)


    def editCut(self):
	textEdit = self.mdi.activeWindow()
        if textEdit is None or not ininstance(textEdit, QTextEdit):
	    return
        cursor = textEdit.textCursor()
	text = cursor.selectedText()
	if not text.isEmpty():
	    cursor.removeSelectedText()
	    clipboard = QApplication.clipboard()
	    clipboard.setText(text)


    def editPaste(self):
        textEdit = self.mdi.activeWindow()
	if textEdit is None or not isinstance(textEdit, QTextEdit):
	    return
        clipboard = QApplication.clipboard()
	textEdit.insertPlainText(clipboard.text())


    def projMake(self, arg=None):
        directory = QDir(self.projdir)
	if ( directory.exists() == False):
	    QMessageBox.warning(self, "Verilator Gui -- Make Error",
                "Failed to open projdir")
	    return
        MakefileExist = False
        listfiles = directory.entryInfoList()
	for i in range(len(listfiles)): 
	    fileInfo = QFileInfo(listfiles[i])
	    if (fileInfo.fileName() == "Makefile" or  
	        fileInfo.fileName() == "makefile"):
                MakefileExist = True
                break
	if MakefileExist == True:
	    command = 'make'
	    if (arg is not None):
	        command = arg
	    # using python os to run shell commands
	    #cur_dir = os.getcwd()
	    #os.chdir(self.projdir)
	    #os.system(command)
	    #os.chdir(cur_dir)
	    # end of using python os to run the shell command

	    # using PyQt to run some shell commands, python os to run 'Makefile'
	    cur_dir = directory.currentPath()
	    directory.setCurrent(self.projdir)
	    os.system(command)
	    directory.setCurrent(cur_dir)
	    # end of using PyQt and python to run shell commands

	    # I don't know which method works better 1/28/2012
	else:
	    QMessageBox.warning(self, "Verilator Gui -- Make Error",
	        "Failed to find Makefile")

	
    def projMakeClean(self):
        self.projMake('make clean')


    def projRun(self):
        self.projMake('make run')

    
    def vcdView(self):
        command = 'dinotrace/dinotrace'
	os.system(command)


    def plot(self):
	error = None
	fh = None
	data = []
	try:
            filename = QFileDialog.getOpenFileName(self, "View -- Open File", self.projdir, \
                ("data files (*.dat *.data);; files (*)"), None, 
		QFileDialog.DontUseNativeDialog)
	    if filename.isEmpty():
	        return
            fh = open(filename, 'r')
	    line = str()
	    for line in fh:
	        d = float(line)
		data.append(d)
	except (IOError, OSError), e:
	    error = "Failed to load: %s" % e
	finally:
	    if fh is not None:
                fh.close()
	    if error is not None:
	        return False, error
	    length = len(data)
	    t = range(length)
	    pylab.plot(t, data)
	    pylab.xlabel('time')
	    pylab.ylabel('data')
	    pylab.title('plot')
	    pylab.grid(True)
	    pylab.show()


    def setCurrentFont(self, arg):
	textEdit = self.mdi.activeWindow()
	textEdit.setCurrentFont(arg)


    def setFontSize(self, arg):
	textEdit = self.mdi.activeWindow()
	size = float(arg)
	textEdit.setFontPointSize(size)


    def windowRestoreAll(self):
        for textEdit in self.mdi.windowList():
	    textEdit.showNormal()


    def windowMinimizeAll(self):
        for textEdit in self.mdi.windowList():
	    textEdit.showMinimized()


    def updateWindowMenu(self):
        self.windowMenu.clear()
	self.addActions(self.windowMenu, (self.windowNextAction, self.windowPrevAction,
	    self.windowCascadeAction, self.windowTileAction, self.windowRestoreAction,
	    self.windowMinimizeAction, self.windowArrangeIconsAction,
	    None, self.windowCloseAction))
	textEdits = self.mdi.windowList()
	if not textEdits:
	    return
        self.windowMenu.addSeparator()
	i = 1
	menu = self.windowMenu
	for textEdit in textEdits:
	    title = textEdit.windowTitle()
	    if i == 10:
	        self.windowMenu.addSeparator()
		menu = menu.addMenu("&More")
	    accel = ""
	    if i < 10:
	        accel = "&{0} ".format(i) 
            elif i < 36:
		accel = "&{0} ".format(chr(i + ord("@") - 9))
	    action = menu.addAction("{0}{1}".format(accel, title))
	    self.connect(action, SIGNAL("triggered()"), self.windowMapper, SLOT("map()"))
	    self.windowMapper.setMapping(action, textEdit)
	    i += 1


    def helpAbout(self):
        QMessageBox.about(self, "About Verilator Gui",
	    """<b> Verilator Gui</b> V{0}
	    <p> Copyright &copy. 2011-11 Jian. All rights reserved.
	    <p> This application can used to perform Verilator Gui.
	    <p> Python {1} - Qt{2} - PyQt{3} on {4}""".format(__version__,
                platform.python_version(), QT_VERSION_STR, PYQT_VERSION_STR,
		platform.system()))


    def helpHelp(self):
        return


def main():
    app = QApplication(sys.argv)
    app.setOrganizationName("Jian")
    app.setOrganizationDomain("4810 Listra Rd")
    app.setApplicationName("Verilator Gui")
    app.setWindowIcon(QIcon(":/icon.png"))
    form = MainWindow()
    form.show()
    app.exec_()

main()
